package com.anjie.powerproject.service.impl;

import com.alibaba.fastjson.JSON;
import com.anjie.powerproject.entity.DynamicProperty;
import com.anjie.powerproject.map.DynamicPropertyMap;
import com.anjie.powerproject.pojo.dto.DynamicPropertyDTO;
import com.anjie.powerproject.pojo.dto.DynamicPropertyDetail;
import com.anjie.powerproject.pojo.dto.DynamicPropertyModifyCommandDTO;
import com.anjie.powerproject.repository.DynamicPropertyRepository;
import com.anjie.powerproject.service.DynamicPropertyService;
import com.anjie.powerproject.util.ExpressUtil;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class DynamicPropertyServiceImpl implements DynamicPropertyService {
    private static final Logger logger = LoggerFactory.getLogger(DynamicPropertyServiceImpl.class);

    @Autowired
    DynamicPropertyRepository dynamicPropertyRepository;

    @Autowired
    DynamicPropertyMap dynamicPropertyMap;

    @Autowired
    ExpressRunner runner;

    @Override
    public List<DynamicPropertyDTO> getall(Integer graphId) {
        List<DynamicPropertyDTO> dynamicProperties = new ArrayList<>();

        List<DynamicProperty> dynamicPropertyList = dynamicPropertyRepository.findByGraphIdEquals(graphId);
        for (DynamicProperty property : dynamicPropertyList) {
            DynamicPropertyDTO dynamicPropertyDTO = dynamicPropertyMap.toDTO(property);
            String express = property.getExpress();
            List<DynamicPropertyDetail> details = ExpressUtil.expressToDTO(express);
            dynamicPropertyDTO.setDetails(details);

            dynamicProperties.add(dynamicPropertyDTO);
        }

        return dynamicProperties;
    }

    @Override
    @Transactional(rollbackOn = Throwable.class)
    public void modify(DynamicPropertyModifyCommandDTO command) {
        List<DynamicPropertyDTO> oldData = getall(command.getGraphId());

        // 做diff，看哪些条目需要插入、哪些条目需要更新、哪些条目需要删除
        List<DynamicPropertyDTO> newData = command.getProperties();

        // 首先没有id的条目一律视为需要插入的
        List<DynamicPropertyDTO> insertDTOList = newData.stream().filter(e -> Objects.isNull(e.getId())).collect(Collectors.toList());
        List<DynamicProperty> insertList = dynamicPropertyMap.toEntity(insertDTOList);

        // oldData里有，但newData里没有的是需要被删除的
        List<Integer> oldIds = oldData.stream().map(DynamicPropertyDTO::getId).collect(Collectors.toList());
        List<Integer> newIds = newData.stream().map(DynamicPropertyDTO::getId).collect(Collectors.toList());
        List<Integer> deleteIds = subtract(oldIds, newIds);
        List<DynamicPropertyDTO> deleteDTOList = oldData.stream().filter(e -> deleteIds.contains(e.getId())).collect(Collectors.toList());
        List<DynamicProperty> deleteList = dynamicPropertyMap.toEntity(deleteDTOList);

        // 对于newData中有id的条目，需要比较是否发生了修改
        List<DynamicPropertyDTO> hasIdDTOList = newData.stream().filter(e -> Objects.nonNull(e.getId())).collect(Collectors.toList());
        Set<String> oldSet = oldData.stream().map(JSON::toJSONString).collect(Collectors.toSet());
        List<DynamicPropertyDTO> updateDTOList = hasIdDTOList.stream().filter(e -> !oldSet.contains(JSON.toJSONString(e))).collect(Collectors.toList());
        List<DynamicProperty> updateList = dynamicPropertyMap.toEntity(updateDTOList);

        dynamicPropertyRepository.saveAll(insertList);
        dynamicPropertyRepository.saveAll(updateList);
        dynamicPropertyRepository.deleteAll(deleteList);
    }

    public static List<Integer> subtract(final List<Integer> a, final List<Integer> b) {
        List<Integer> list = new ArrayList<>(a);
        for (Iterator<Integer> it = b.iterator(); it.hasNext();) {
            list.remove(it.next());
        }
        return list;
    }

    @Override
    public Boolean excute(Integer id) {
        DynamicProperty property = dynamicPropertyRepository.getOne(id);
        if (property == null) {
            return null;
        } else {
            Object result = null;
            try {
                result = runner.execute(property.getExpress(), new DefaultContext<>(), new ArrayList<>(), true, false);
            } catch (Exception e) {
                throw new RuntimeException("表达式执行失败:" + e.getMessage());
            }
            return (Boolean) result;
        }
    }
}
